/**
 * Yago.js
 *
 * lxrmido@lxrmido.com
 *
 *      inv 140523a 
 * 		inv 140701a 初步完成carousel
 * 		inv 140702a 修改carousel
 * 		inv 141117a 添加tapRect
 *      inv 150109a 修改scroll添加scrollbar，修复滚动流畅1
 *      inv 150110a 修改initNodes
 *      inv 150127a requirejs
 */

define(function() {
	var readyList = [];
	var styleNode;
	var cssPrefix = 'not-support';
	var transformAttrName = '';
    var transitionAttrName = '';

	var Yago = function(a, b, c) {
		if (typeof a == "function") {
			readyList.push(a);
		}
	};

	Yago.wrap = {
		// 适应内容宽度
		horizon: function(element) {
			var i = 0,
				sum = 0,
				l = element.children.length,
				nd, cs;
			for (; i < l; i++) {
				nd = element.children[i];
				cs = window.getComputedStyle(nd);
				if (cs) {
					sum += Yago.css.computeOuterWidth(cs);
				}
			}
			element.style.width = sum + 'px';
			return sum;
		},
		// 适应内容高度
		vertical: function(element) {
			var i = 0,
				sum = 0,
				l = element.children.length,
				nd, cs;
			for (; i < l; i++) {
				nd = element.children[i];
				cs = window.getComputedStyle(nd);
				if (cs) {
					sum += Yago.css.computeOuterHeight(cs);
				}
			}
			element.style.height = sum + 'px';
			return sum;
		}
	};

	Yago.touch = {

		showTapRect: function(tapRect, kono, ex, ey) {
			var x = 0,
				y = 0,
				d = kono,
				w, h, cs;

			// 1
			var box = kono.getBoundingClientRect();
			x = box.left + window.pageXOffset - document.documentElement.clientLeft;
			y = box.top + window.pageYOffset - document.documentElement.clientTop;

			// 2
			//			while(d && !isNaN(d.offsetLeft) && !isNaN(d.offsetTop)){
			//				x += d.offsetLeft;
			//				y += d.offsetTop;
			//				d = d.parentNode;
			//			}

			cs = window.getComputedStyle(kono);
			w = parseInt(cs.width);
			h = parseInt(cs.height);
			if (w > h) {
				h = w;
			} else {
				w = h;
			}
			tapRect.style.webkitTransition = '';
			tapRect.style.display = 'block';
			tapRect.style.top = (ey - y - h / 2) + 'px';
			tapRect.style.left = (ex - x - w / 2) + 'px';
			tapRect.style.width = w + 'px';
			tapRect.style.height = h + 'px';
			tapRect.style.opacity = 1;
			tapRect.style.webkitTransform = 'scale(0.1, 0.1)';
			setTimeout(function() {
				tapRect.style.webkitTransition = 'all ease-out 0.2s';
				tapRect.style.webkitTransform = 'scale(1, 1)';
			}, 10);
		},

		hideTapRect: function(tapRect) {
			tapRect.style.webkitTransition = 'all ease-out 0.4s';
			tapRect.style.webkitTransform = 'scale(2, 2)';
			tapRect.style.opacity = 0;
			setTimeout(function() {
				tapRect.style.display = 'none';
			}, 200);
		},

		tap: function(element, tickNeed, func, condition, unsync) {
			var touches, startTick, endTick, tapRect;
			var sx, sy;

			function cc(d) {
				d.removeEventListener('touchmove', onmv);
				d.removeEventListener('touchend', onend);
				d.className = d.className.replace(' tap-active', '');
				if (tapRect) {
					Yago.touch.hideTapRect(tapRect);
				}
			}

			function onmv(e) {
				var dt;
				dt = e.touches[0].clientX - sx;
				if (dt > 5 || dt < -5) {
					cc(this);
					return;
				}
				dt = e.touches[0].clientY - sy;
				if (dt > 5 || dt < -5) {
					cc(this);
				}
			}

			function onend(e) {
				var ret, that;
				that = this;
				e.touches = touches;
				e.startTick = startTick;
				e.endTick = tick;
				e.tickDiff = e.endTick - e.startTick;
				e.tapX = touches[0].clientX;
				e.tapY = touches[0].clientY;
				if (e.tickDiff > tickNeed) {
					if (unsync) {
						if(tapRect){
							setTimeout(function() {
								func.call(that, e);
							}, 200);
						}else{
							setTimeout(function() {
								func.call(that, e);
							}, 20);
						}
					} else {
						ret = func.call(that, e);
					}
				}
				cc(this);
				return ret;				
			}
			element.addEventListener('touchstart', function(e) {
				var d = e.target;
				var parse = true;
				sx = e.touches[0].clientX;
				sy = e.touches[0].clientY;
				if (condition) {
					parse = false;
					while (d && d != element) {
						if (Array.prototype.indexOf.call(d.classList, condition) >= 0) {
							parse = true;
							break;
						} else {
							d = d.parentNode;
						}
					}
				} else {
					d = this;
				}
				if (parse) {
					touches = e.touches;
					startTick = tick;
					d.addEventListener('touchmove', onmv);
					d.addEventListener('touchend', onend);
					d.className += ' tap-active';
					tapRect = d.children[d.children.length - 1];
					if (tapRect && tapRect.className && tapRect.className == 'tap-rect') {
						Yago.touch.showTapRect(tapRect, d, e.touches[0].clientX, e.touches[0].clientY);
					} else {
						tapRect = null;
					}
					e.preventDefault();
				}
			});
		},

		// 弹性滚动
		enableScroll: function(element, direction, config) {
			var default_config,
				i, isHorizon;
			isHorizon = (direction == 'x');
			default_config = {
				wrap: true, // 是否自动计算大小
				scrollbar : null, // 滚动条
				overstepCallback: null, // 内容被拖动至越界时的回调
				releaseCallback: null, // 松开时的回调
                speedAcc : 1,
                accAcc : 0.02,
                preventDefault : false,
                stopPropagation : false,
                returnFalse : false
			};
			config = config || {};
			for (i in default_config) {
				if (!(i in config)) {
					config[i] = default_config[i];
				}
			}
			var mv = element.mv = {
				mvtype: 'scroll',
				start: 0, // 触摸起始点
				end: 0, // 触摸结束点
				delta: 0, // 触摸位移
				current: 0, // 当前坐标
				another: 0, // 开始触摸时另一个坐标
				offset: 0, // 内容偏移坐标
				startTick: 0, // 开始触摸时间
				endTick: 0, // 触摸结束时间
				speedAcc: 0, // 加速度,
				speed: 0, // 速度
				limit: 0,
				max: 0,
				visiScope: 0,
				animating: false, // 是否继续动画
				backing: false,
				overstep: false,
				config: config,
				reWrap: function() {
					var widthInAll,
						parentCs,
						parentWidth,
						limit;
					widthInAll = isHorizon ? Yago.wrap.horizon(element) : Yago.wrap.vertical(element);
					parentCs = window.getComputedStyle(element.parentNode);
					parentWidth = isHorizon ? parseInt(parentCs.width) : parseInt(parentCs.height);
					limit = widthInAll > parentWidth ? parentWidth - widthInAll : 0;
					mv.limit = limit;
					mv.visiScope = parentWidth;
					mv.max = widthInAll;
					if(mv.config.scrollbar){
						mv.scrollRate   = parentWidth / widthInAll;
						mv.scrollLength = parseInt(mv.scrollRate * parentWidth);
						config.scrollbar.style[isHorizon ? 'width' : 'height'] = element.mv.scrollLength + 'px';
					}
                    if(mv.offset < limit){
                        mv.offset = limit;
                        if(isHorizon){
                            applyMoveX(element, mv.offset);
                        }else{
                            applyMoveY(element, mv.offset);
                        }
                    }
				},
				scrollTo: function(target, fs, callback) {
					mv._cbScroll = callback;
					scrollToInit(element, target, fs);
					if (isHorizon) {
						motionTweenScrollToX(element);
					} else {
						motionTweenScrollToY(element);
					}
				}
			};
			if (config.wrap) {
				mv.reWrap();
			}
			element.addEventListener('touchstart', function(e) {
				if (mv.disabled || mv.backing || mv.overstep) {
					return;
				}
				mv.start = isHorizon ? e.touches[0].clientX : e.touches[0].clientY;
				mv.another = isHorizon ? e.touches[0].clientY : e.touches[0].clientX;
				mv.startTick = tick;
				mv.delta = 0;
				mv.speed = 0;
				mv.speedAcc = 0;
				mv.animating = false;
				element.addEventListener('touchmove', isHorizon ? firstTouchMoveX : firstTouchMoveY);
				element.addEventListener('touchend', isHorizon ? firstTouchEndX : firstTouchEndY);
				if (config.preventDefault) {
					e.preventDefault();
				}
                if (config.stopPropagation) {
					e.stopPropagation();
				}
                if (config.returnFalse) {
					return false;
				}
			});
		},
		// 位置停留滑动
		enableSlide: function(element, direction, config) {
			var default_config,
				i, isHorizon;
			isHorizon = (direction == 'x');
			default_config = {
				min: 0,
				max: isHorizon ? Yago.scrW : Yago.scrH,
				bound: 60,
				stop: 60,
				accMin: 0,
				accAuto: 0.1,
				accBack: 10,
				speedAuto: 20,
				speedMin: 10,
				moveCallback: null,
				moveEndCallback: null,
                preventDefault : false,
                stopPropagation : false,
                returnFalse : false
			};
			config = config || {};
			for (i in default_config) {
				if (!(i in config)) {
					config[i] = default_config[i];
				}
			}
			var mv = element.mv = {
				mvtype: 'slide',
				start: 0, // 触摸起始点
				end: 0, // 触摸结束点
				delta: 0, // 触摸位移
				current: 0, // 当前坐标
				another: 0, // 开始触摸时另一个坐标
				offset: 0, // 内容偏移坐标
				target: 0,
				targetDirection: 'none',
				startTick: 0, // 开始触摸时间
				endTick: 0, // 触摸结束时间
				speedAcc: 0, // 加速度
				speed: 0, // 速度
				disabled: false,
				animating: false,
				config: config,
				slideTo: function(side, acc, speed) {
					acc = acc || mv.config.accAuto;
					speed = speed || mv.config.speedAuto;
					mv.targetDirection = side;
					switch (side) {
						case 'left':
						case 'top':
							mv.target   = mv.config.min - mv.config.max + mv.config.stop;
							mv.speedAcc = acc;
							mv.speed    = -speed;
							break;
						case 'right':
						case 'bottom':
							mv.target   = mv.config.max - mv.config.stop;
							mv.speedAcc = -acc;
							mv.speed    = speed
							break;
						default:
							if (mv.offset > element.mv.config.min + element.mv.config.bound) {
								mv.speedAcc = acc;
								mv.speed = -speed;
							} else {
								mv.speedAcc = -acc;
								mv.speed = speed;
							}
							mv.target = mv.config.min;
							break;
					}
					mv.animating = true;
					if (isHorizon) {
						motionTweenDecSpeedX(element);
					} else {
						motionTweenDecSpeedY(element);
					}
				}
			};
			element.addEventListener('touchstart', function(e) {
				if (mv.disabled || mv.animating) {
					return;
				}
				mv.start = isHorizon ? e.touches[0].clientX : e.touches[0].clientY;
				mv.another = isHorizon ? e.touches[0].clientY : e.touches[0].clientX;
				mv.startTick = tick;
				mv.delta = 0;
				element.addEventListener('touchmove', isHorizon ? firstTouchMoveX : firstTouchMoveY);
				element.addEventListener('touchend', isHorizon ? firstTouchEndX : firstTouchEndY);
				if (config.preventDefault) {
					e.preventDefault();
				}
                if (config.stopPropagation) {
					e.stopPropagation();
				}
                if (config.returnFalse) {
					return false;
				}
			});
		},
		// 轮播式滑动
		enableCarousel: function(element, direction, config) {
			var default_config,
				i, isHorizon;
			isHorizon = (direction == 'x');
			default_config = {
				min: 0,
				step: isHorizon ? Yago.scrW : Yago.scrH,
				moveCallback: null,
				moveEndCallback: null,
				speed: 10,
				speedAcc: -0.1,
				frames: 30,
                // 滑动操作距离达到step * threshold时，滑到上一个/下一个
                threshold : 0.25,
                preventDefault : false,
                stopPropagation : false,
                returnFalse : false
			};
			config = config || {};
			var mv = element.mv = {
				mvtype: 'carousel',
				start: 0, // 触摸起始点
				end: 0, // 触摸结束点
				delta: 0, // 触摸位移
				current: 0, // 当前坐标
				another: 0, // 开始触摸时另一个坐标
				offset: 0, // 内容偏移坐标
				target: 0,
				before: 0,
				targetDirection: 'none',
				startTick: 0, // 开始触摸时间
				endTick: 0, // 触摸结束时间
				speedAcc: 0, // 加速度
				speed: 0, // 速度
				disabled: false,
				animating: false,
				config: config,
				reWrap: function() {
					var widthInAll,
						parentCs,
						parentWidth,
						limit;
					widthInAll = isHorizon ? Yago.wrap.horizon(element) : Yago.wrap.vertical(element);
					parentCs = window.getComputedStyle(element.parentNode);
					parentWidth = isHorizon ? parseInt(parentCs.width) : parseInt(parentCs.height);
					limit = widthInAll > parentWidth ? parentWidth - widthInAll : 0;
					mv.limit     = limit;
					mv.visiScope = parentWidth;
					mv.max       = widthInAll;
					default_config.step = parentWidth;
				},
				carouselTo: function(targetStep, t, endCallBack) {
					var a, v, s;
					mv.target = -targetStep * config.step;
					s = Math.abs(mv.target - mv.offset);
					if (s == 0) {
						if (endCallBack) {
							endCallBack();
						}
						return;
					}
                    // 滚动到指定地点需要的帧数
					t = t || config.frames;
                    // 根据 s = 1/2*at²
					a = s / (t * t);
					v = a * t * 2;
					if (targetStep <= 0) {
						targetStep = 0;
					}
					mv.speed    = v;
					mv.speedAcc = -a;

					if (mv.target < element.mv.offset) {
						mv.speedAcc = -mv.speedAcc;
						mv.speed    = -mv.speed;
					}

					if (endCallBack) {
						mv.endCallBack = endCallBack;
					}

					if (isHorizon) {
						motionTweenDecSpeedX(element);
					}
				}
			};
			element.mv.reWrap();
			for (i in default_config) {
				if (!(i in config)) {
					config[i] = default_config[i];
				}
			}
			element.addEventListener('touchstart', function(e) {
				if (mv.disabled || mv.animating) {
					return;
				}
				mv.before = this.mv.offset;
				mv.start = isHorizon ? e.touches[0].clientX : e.touches[0].clientY;
				mv.another = isHorizon ? e.touches[0].clientY : e.touches[0].clientX;
				mv.startTick = tick;
				mv.delta = 0;
				this.addEventListener('touchmove', isHorizon ? firstTouchMoveX : firstTouchMoveY);
				this.addEventListener('touchend', isHorizon ? firstTouchEndX : firstTouchEndY);
				if (mv.config.preventDefault) {
					e.preventDefault();
				}
                if (mv.config.stopPropagation) {
					e.stopPropagation();
				}
                if (mv.config.returnFalse) {
					return false;
				}
			});
		}
	};

	function firstTouchMoveX(e) {
		var x, y;
		x = Math.abs(e.touches[0].clientX - this.mv.start);
		y = Math.abs(e.touches[0].clientY - this.mv.another);
		this.removeEventListener('touchmove', firstTouchMoveX);
		this.removeEventListener('touchend', firstTouchEndX);
		if (y < x) {
			switch (this.mv.mvtype) {
				case 'scroll':
					this.addEventListener('touchmove', scrollMoveX);
					this.addEventListener('touchend', scrollEndX);
					break;
				case 'slide':
					this.addEventListener('touchmove', slideMoveX);
					this.addEventListener('touchend', slideEndX);
					break;
				case 'carousel':
					this.addEventListener('touchmove', carouselMoveX);
					this.addEventListener('touchend', carouselEndX);
					break;
				default:
					break;
			}
		}
	}

	function firstTouchMoveY(e) {
		var x, y;
		x = Math.abs(e.touches[0].clientX - this.mv.another);
		y = Math.abs(e.touches[0].clientY - this.mv.start);
		this.removeEventListener('touchmove', firstTouchMoveY);
		this.removeEventListener('touchend', firstTouchEndY);
		if (y > x) {
			switch (this.mv.mvtype) {
				case 'scroll':
					this.addEventListener('touchmove', scrollMoveY);
					this.addEventListener('touchend', scrollEndY);
					break;
				case 'slide':
					this.addEventListener('touchmove', slideMoveY);
					this.addEventListener('touchend', slideEndY);
					break;
				case 'carousel':
					this.addEventListener('touchmove', carouselMoveY);
					this.addEventListener('touchend', carouselEndY);
					break;
				default:
					break;
			}
		}
	}

	function firstTouchEndX(e) {
		this.removeEventListener('touchmove', firstTouchMoveX);
		this.removeEventListener('touchend', firstTouchEndX);
	}

	function firstTouchEndY(e) {
		this.removeEventListener('touchmove', firstTouchMoveY);
		this.removeEventListener('touchend', firstTouchEndY);
	}

	function slideMoveGeneral(element) {
		var o, os = false, mv = element.mv;
		mv.delta = mv.current - mv.start;
		o = mv.offset + mv.delta;
		if (mv.config.moveCallback) {
			mv.config.moveCallback(o);
		}
		return o;
	}

	function carouselMoveGeneral(element) {
		var o, os = false, mv = element.mv;
		mv.delta = mv.current - mv.start;
		o = mv.offset + mv.delta;
		if (mv.config.moveCallback) {
			mv.config.moveCallback(o);
		}
		return o;
	}

	function slideMoveX(e) {
		this.mv.current = e.touches[0].clientX;
		applyMoveX(this, slideMoveGeneral(this));
	}

	function carouselMoveX(e) {
		this.mv.current = e.touches[0].clientX;
        if(this.mv.current > Yago.scrW || this.mv.current < 0){
            return carouselEndX.apply(this, [e]);
        }
		applyMoveX(this, carouselMoveGeneral(this));
	}
    
    function carouselMoveY(e) {
		this.mv.current = e.touches[0].clientY;
        if(this.mv.current > Yago.scrH || this.mv.current < 0){
            return carouselEndY.apply(this, [e]);
        }
		applyMoveY(this, carouselMoveGeneral(this));
	}

	function slideMoveY(e) {
		this.mv.current = e.touches[0].clientY;
		applyMoveY(this, slideMoveGeneral(this));
	}

	function scrollMoveGeneral(element) {
		var o, os = false, mv = element.mv;
		mv.delta = mv.current - mv.start;
		o = mv.offset + mv.delta;
		if (o > 0) {
			o = (mv.offset + mv.delta) / 3;
			if (mv.config.overstepCallback) {
				mv.config.overstepCallback(o);
			}
			os = true;
		} else if (o < mv.limit) {
			o = mv.limit + (mv.offset + mv.delta - mv.limit) / 3;
			if (mv.config.overstepCallback) {
				mv.config.overstepCallback(o - mv.limit);
			}
			os = true;
		}
		return o;
	}

	function scrollMoveX(e) {
        var o, mv = this.mv;
		mv.current = e.touches[0].clientX;
        if(mv.current > Yago.scrW || mv.current < 0){
            return scrollEndX.apply(this, [e]);
        }
		o = scrollMoveGeneral(this);
		if(mv.config.scrollbar){
			mv.config.scrollbar.style.opacity = 1;
			applyMoveX(mv.config.scrollbar, - o * mv.scrollRate);
		}
		applyMoveX(this, o);
	}

	function scrollMoveY(e) {
        var o, mv = this.mv;
		mv.current = e.touches[0].clientY;
        if(mv.current > Yago.scrH || mv.current < 0){
            return scrollEndY.apply(this, [e]);
        }
		o = scrollMoveGeneral(this);
		if(mv.config.scrollbar){
			mv.config.scrollbar.style.opacity = 1;
			applyMoveY(mv.config.scrollbar, - o * mv.scrollRate);
		}
		applyMoveY(this, o);
	}

	function slideEndGeneral(element) {
		var o, tickDiff, s, a, t,
            mv = element.mv,
            config = mv.config;
		tickDiff = tick - mv.startTick;
		o = mv.offset + mv.delta;
		mv.offset = o;
		mv.speed = mv.delta / tickDiff;
		if (mv.speed > 0 && mv.offset > config.min + config.bound && mv.offset < config.max) { 
            // 页面停在中央、左向右滑、超过临界点
			mv.targetDirection = 'right';
			s = config.max - mv.offset;
			a = mv.speed * mv.speed / 2 / s;
			mv.target = config.max - config.stop;
			mv.speedAcc = -(a < config.accMin ? config.accMin : a);
			mv.animation = true;
		} else
		if (mv.speed < 0 && mv.offset < config.max - config.bound && mv.offset > config.min) { 
            // 页面停在右侧、右向左滑、超过临界点
			mv.targetDirection = 'right-to-center';
			s = mv.offset - config.min;
			a = mv.speed  * mv.speed / 2 / s;
			mv.target    = config.min;
			mv.speedAcc  = a < config.accMin ? config.accMin : a;
			mv.animation = true;
		} else
		if (mv.speed < 0 && mv.offset < -mv.config.bound) { 
            // 页面停在中央、右向左滑、超过临界点
			mv.targetDirection = 'left';
			s = config.max - config.min + config.bound;
			a = mv.speed * mv.speed / 2 / s;
			mv.target    = config.min - config.max + config.stop;
			mv.speedAcc  = a < config.accMin ? config.accMin : a;
			mv.animation = true;
		} else
		if (mv.speed > 0 && mv.offset > config.min - config.max + config.bound && mv.offset < config.max - config.min - config.bound) { 
            // 页面停在左侧，左向右滑，超过临界点
			mv.targetDirection = 'left-to-center';
			s = config.max - config.min + config.bound;
			a = mv.speed * mv.speed / 2 / s;
			mv.target    = config.min;
			mv.speedAcc  = -(a < config.accMin ? config.accMin : a);
			mv.animation = true;
		} else
		if (mv.speed > 0 && mv.offset < config.max) {
			mv.target    = config.min;
			mv.speedAcc  = - config.accBack;
			mv.animation = true;
		} else if (mv.speed < 0 && mv.offset > config.min) {
			mv.target    = config.max - config.stop;
			mv.speedAcc  = config.accBack;
			mv.animation = true;
		} else if (element.mv.speed > 0) {
			mv.target    = config.max - config.stop;
			mv.speedAcc  = - config.accBack;
			mv.animation = true;
		} else if (element.mv.speed < 0) {
			mv.target    = config.min;
			mv.speedAcc  = config.accBack;
			mv.animation = true;
		} else {
			mv.animation = false;
		}
		if (mv.speed > 0) {
			if (mv.speed < config.speedMin) {
				mv.speed = config.speedMin;
			}
		} else if (element.mv.speed < 0) {
			if (mv.speed > - config.speedMin) {
				mv.speed = - config.speedMin;
			}
		}
	}

	function carouselEndGeneral(element) {
		var target, x, k, s, qs, mx,
            mv     = element.mv,
            config = mv.config;
		if (mv.offset > 0) {
			target = 0;
		} else if (mv.offset < mv.limit) {
			target = mv.limit;
		} else {
			s = Math.abs(mv.offset % config.step);
			if (s == 0) {
				target = mv.offset;
			} else {
                // 判断滑动距离是否足够滑到下一页
				qs = Math.floor(config.step * config.threshold);
				k  = Math.ceil(mv.offset / config.step);
				if (mv.offset > mv.before) { // →
					if (mv.offset - mv.before > qs) {
						if (k > 0) {
							target = (k + 1) * config.step;
						} else {
							target = k * config.step;
						}
					} else {
						target = mv.before;
					}
				} else { // ←
					if (mv.before - mv.offset > qs) {
						target = (k - 1) * config.step;
						if (target < mv.limit) {
							target = mv.limit;
						}
					} else {
						target = mv.before;
					}
				}
			}
		}
		mv.speed    = config.speed;
		mv.speedAcc = config.speedAcc;
		if (target < mv.offset) {
			mv.speedAcc = - mv.speedAcc;
            mv.speed    = - mv.speed;
		}
		mv.target = target;
	}

	function carouselEndX(e) {
		this.removeEventListener('touchmove', carouselMoveX);
		this.removeEventListener('touchend',  carouselEndX);
		this.mv.offset += this.mv.delta;
		carouselEndGeneral(this);
		motionTweenDecSpeedX(this);
	}
    
    function carouselEndY(e) {
		this.removeEventListener('touchmove', carouselMoveY);
		this.removeEventListener('touchend',  carouselEndY);
		this.mv.offset += this.mv.delta;
		carouselEndGeneral(this);
		motionTweenDecSpeedY(this);
	}

	function slideEndX(e) {
		this.removeEventListener('touchmove', slideMoveX);
		this.removeEventListener('touchend',  slideEndX);
		slideEndGeneral(this);
        this.mv.animation && motionTweenDecSpeedX(this);
	}

	function slideEndY(e) {
		this.removeEventListener('touchmove', slideMoveY);
		this.removeEventListener('touchend',  slideEndY);
		slideEndGeneral(this);
        this.mv.animation && motionTweenDecSpeedY(this);
	}

	function scrollEndGeneral(element) {
		var tickDiff, avgSpeed, o, os = false,
            mv = element.mv,
            config = mv.config;
		element.removeEventListener('touchmove', scrollMoveX);
		element.removeEventListener('touchmove', scrollMoveY);
		element.removeEventListener('touchend',  scrollEndX);
		element.removeEventListener('touchend',  scrollEndY);
		o  = element.mv.offset + element.mv.delta;
        os = o > 0 || o < element.mv.limit;
		mv.offset = os ? (mv.offset + mv.delta / 3) : o;
        config.releaseCallback && config.releaseCallback(mv.offset)
		mv.endTick = tick;
		tickDiff = tick - mv.startTick;
		avgSpeed = os ? (mv.delta / tickDiff / 3) : (mv.delta / tickDiff);
		if (config.maxSpeed) {
			if (avgSpeed > config.maxSpeed) {
				avgSpeed = config.maxSpeed;
			} else if (avgSpeed < -config.maxSpeed) {
				avgSpeed = -config.maxSpeed;
			}
		}

		mv.speed = avgSpeed;
		if (avgSpeed > 0) {
			mv.speedAcc = - mv.config.speedAcc;
		} else if (avgSpeed < 0) {
			mv.speedAcc = config.speedAcc;
		}
		
		
	}

	function scrollEndX(e) {
		scrollEndGeneral(this);
		if (this.mv.speed == 0) {

		} else {
			this.mv.animating = true;
			motionTweenAnimateX(this);
		}
	}

	function scrollEndY(e) {
		scrollEndGeneral(this);
		if (this.mv.speed == 0) {

		} else {
			this.mv.animating = true;
			motionTweenAnimateY(this);
            animateMoveY(this);
		}
	}

    function animateMoveY(element){
        applyMoveY(element, element.mv.offset);
        element.mv.animating && animate(function(){animateMoveY(element)});
    }
    
	function applyMoveX(element, x) {
		element.style[transformAttrName] = "translateX(" + parseInt(x) + "px)";
	}

	function applyMoveY(element, y) {
		element.style[transformAttrName] = "translateY(" + parseInt(y) + "px)";
	}

	function scrollToInit(element, target, fs) {
        var mv = element.mv;
		mv.motionStart     = mv.offset;
		mv.motionTarget    = target;
		mv.framesInAll     = fs;
		mv.framesToReverse = Math.floor(fs / 2);
		mv.currentFrame    = 0;
		mv.reverseProc     = false;
		mv.speedAcc        = (4 * (target - mv.motionStart)) / (fs * fs);
		mv.speed           = 0;
	}

	function motionTweenDecSpeed(element) {
		var goOn, oldSpeed,
            mv = element.mv;
		goOn = true;
		oldSpeed = mv.speed;
		mv.speed  += mv.speedAcc;
		mv.offset += mv.speed;
		if (mv.speedAcc < 0) { // left to right
			if (mv.offset >= mv.target) {
				goOn = false;
			}
		} else {
			if (mv.offset <= mv.target) {
				goOn = false;
			}
		}
		if (oldSpeed <= 0 && mv.speed >= 0) {
			goOn = false;
		} else if (oldSpeed >= 0 && mv.speed <= 0) {
			goOn = false;
		}
		if (!goOn) {
			mv.offset = mv.target;
		}
		if (mv.config.moveCallback) {
			mv.config.moveCallback(mv.offset);
		}
		mv.animating = goOn;
	}

	function motionTweenDecSpeedX(element) {
        var mv = element.mv,
            config = mv.config;
		motionTweenDecSpeed(element);
		if (mv.animating) {
			applyMoveX(element, mv.offset);
			animate(function() {
				motionTweenDecSpeedX(element);
			});
		} else {
			applyMoveX(element, mv.offset);
            config.moveEndCallback && config.moveEndCallback(mv.targetDirection, mv.offset);
            config.carouselEndCallback && config.carouselEndCallback(-mv.offset / config.step);
			if (mv.endCallBack) {
				setTimeout(mv.endCallBack, 100);
				mv.endCallBack = null;
			}
			mv.animating = false;
		}
	}

	function motionTweenDecSpeedY(element) {
		motionTweenDecSpeed(element);
		if (element.mv.animating) {
			applyMoveY(element, element.mv.offset);
			animate(function() {
				motionTweenDecSpeedY(element);
			});
		} else {
			applyMoveY(element, element.mv.offset);
			element.mv.animating = false;
		}
	}

	function motionTweenScrollToX(element) {
        var mv = element.mv;
		if (mv.animating) {
			return;
		}
		var goOn = mv.currentFrame < mv.framesInAll;
		mv.currentFrame++;
		if (mv.reverseProc) {
			if (mv.speed > 0 && mv.offset >= mv.target) {
				goOn = false;
			} else if (mv.speed < 0 && mv.offset <= mv.target) {
				goOn = false;
			}
		} else {
			if (mv.currentFrame > mv.framesToReverse) {
				mv.reverseProc = true;
				mv.speedAcc    = -mv.speedAcc;
			}
		}
		if (goOn) {
			mv.speed  += mv.speedAcc;
			mv.offset += mv.speed;
			applyMoveX(element, mv.offset);
			animate(function() {
				motionTweenScrollToX(element);
			});
		} else {
			element.mv.offset = mv.motionTarget;
			applyMoveX(element, mv.offset);
            mv._cbScroll && mv._cbScroll();
		}
	}

	function motionTweenScrollToY(element) {
        var mv = element.mv;
		if (mv.animating) {
			return;
		}
		var goOn = mv.currentFrame < mv.framesInAll;
		mv.currentFrame++;
		if (mv.reverseProc) {
			if (mv.speed > 0 && mv.offset >= mv.target) {
				goOn = false;
			} else if (mv.speed < 0 && mv.offset <= mv.target) {
				goOn = false;
			}
		} else {
			if (mv.currentFrame > mv.framesToReverse) {
				mv.reverseProc = true;
				mv.speedAcc    = -mv.speedAcc;
			}
		}
		if (goOn) {
			mv.speed  += mv.speedAcc;
			mv.offset += mv.speed;
			applyMoveY(element, mv.offset);
			animate(function() {
				motionTweenScrollToY(element);
			});
		} else {
			mv.offset = mv.motionTarget;
			applyMoveY(element, mv.offset);
			if (mv._cbScroll) {
				mv._cbScroll();
			}
		}
	}

	function motionTweenAnimateGeneral(element) {
        var mv = element.mv;
		var oldSpeed = mv.speed;
        mv.overstep = mv.offset > 0 || mv.offset < mv.limit;
        mv.speed  += mv.speedAcc;
        mv.offset += mv.speed;
		if (
			(mv.speed <= 0 && oldSpeed >= 0) ||
			(mv.speed >= 0 && oldSpeed <= 0)
		) {
			if (mv.overstep) {
				mv.backing   = true;
			} else {
				mv.speed     = 0;
				mv.animating = false;
				mv.overstep  = false;
                mv.config.animateEndCallback && mv.config.animateEndCallback(mv.offset);
			}
		}
		if (mv.backing) {
			if (mv.speedAcc < 0) {
                mv.speedAcc -= mv.config.accAcc;

				if (mv.offset <= 0) {
					mv.offset    = 0;
					mv.speed     = 0;
					mv.backing   = false;
					mv.animating = false;
					mv.overstep  = false;
				}
			} else {
                element.mv.speedAcc += mv.config.accAcc;
				if (mv.offset >= mv.limit) {
					mv.offset    = mv.limit;
					mv.speed     = 0;
					mv.backing   = false;
					mv.animating = false;
					mv.overstep  = false;
				}
			}
		}
        if(mv.offset > mv.visiScope / 2){
            mv.offset = mv.visiScope / 2;
            mv.speed  = 0;
        }else if(mv.offset < mv.limit - mv.visiScope / 2){
            mv.offset = mv.limit - mv.visiScope / 2;
            mv.speed  = 0;
        }
        if(!mv.animating){
            if(mv.config.scrollbar){
                mv.config.scrollbar.style.opacity = 0;
            }
        }
	}

	function motionTweenAnimateX(element) {
        var mv = element.mv;
		motionTweenAnimateGeneral(element);
		applyMoveX(element, mv.offset);
        mv.config.scrollbar && applyMoveX(mv.config.scrollbar, - mv.offset * mv.scrollRate);
        mv.animating && animate(function() { motionTweenAnimateX(element); });
	}

	function motionTweenAnimateY(element) {
        var mv = element.mv;
		motionTweenAnimateGeneral(element);
        mv.config.scrollbar && applyMoveY(mv.config.scrollbar, - mv.offset * mv.scrollRate);
		mv.animating && setTimeout(function() { motionTweenAnimateY(element); }, 20);
	}
	var animate = window.requestAnimationFrame       ||
		          window.webkitRequestAnimationFrame ||
        function(callback) {
            setTimeout(callback, 20)
        };
	var tick = 0;

	function clock() {
		tick++;
		setTimeout(clock, 20);
	}
	Yago.getTick = function() {
		return tick;
	};

	Yago.css = {
		build: function() {
			styleNode = document.createElement("style");
			styleNode.type = "text/css";
			document.getElementsByTagName("head")[0].appendChild(styleNode);
		},
		/**
		 * 添加CSS样式
		 */
		add: function(cssText) {
			return styleNode.sheet.insertRule(cssText, styleNode.sheet.rules.length);
		},
		del: function(index) {
			styleNode.sheet.deletRule(index);
		},
		node: function() {
			return styleNode;
		},
		outerWidth: function(dom) {
			return Yago.css.computeOuterWidth(window.getComputedStyle(dom));
		},
		outerHeight: function(dom) {
			return Yago.css.computeOuterHeight(window.getComputedStyle(dom));
		},
		computeOuterWidth: function(computedStyle) {
			if (computedStyle.boxSizing && computedStyle.boxSizing == 'border-box') {
				return parseInt(computedStyle.width);
			}
			return Yago.css.computedCumulate(computedStyle, [
				'borderLeftWidth', 'borderRightWidth',
				'marginLeft', 'marginRight',
				'paddingLeft', 'paddingRight', 'width'
			]);
		},
		computeOuterHeight: function(computedStyle) {
			if (computedStyle.boxSizing && computedStyle.boxSizing == 'border-box') {
				return parseInt(computedStyle.height);
			}
			return Yago.css.computedCumulate(computedStyle, [
				'borderTopWidth', 'borderBottomWidth',
				'marginTop', 'marginBottom',
				'paddingTop', 'paddingBottom', 'height'
			]);
		},
		computedCumulate: function(computedStyle, members) {
			var i = 0,
				sum = 0;
			for (; i < members.length; i++) {
				sum += parseInt(computedStyle[members[i]]);
			}
			return sum;
		},
        moveX : applyMoveX,
        moveY : applyMoveY
	};

	/**
	 * 解析文档的data-node节点
	 * 传入一个DOM，将DOM下带有data-node的节点逐级挂在DOM上
	 */
	Yago.initNodes = function(obj, nodeCallback) {
		var i, nd;
        if (!obj.childNodes) {
			return obj;
		}
        nodeCallback && nodeCallback(obj);
		if (obj.childNodes.length == 0) {
			return obj;
		}
		for (i = 0; i < obj.childNodes.length; i++) {
			if (obj.childNodes[i].dataset && obj.childNodes[i].dataset.node) {
				obj[obj.childNodes[i].dataset.node] = Yago.initNodes(obj.childNodes[i], nodeCallback);
			}
		}
		return obj;
	};

	function computeSize() {
		Yago.scrW = document.documentElement.clientWidth;
		Yago.scrH = document.documentElement.clientHeight;
	}

	function makeReady() {
		var i, d,
			availPrefix = ['t', 'webkitT', 'MozT', 'msT', 'OT'];
		Yago.css.build();

		d = document.createElement('div');
		for (i = 0; i < availPrefix.length; i++) {
			if ((availPrefix[i] + 'ransform') in d.style) {
				Yago.cssPrefix =
					cssPrefix =
						availPrefix[i].substr(0, availPrefix[i].length - 1);
				if (i > 0) {
					Yago.transformAttrName  = transformAttrName  = cssPrefix + 'Transform';
					Yago.transitionAttrName = transitionAttrName = cssPrefix + 'Transition';
				} else {
					Yago.transformAttrName  = transformAttrName = 'transform';
                    Yago.transitionAttrName = transitionAttrName = 'transition';
				}
				break;
			}
		}

		computeSize();
		clock();
		/**
		 * 最后
		 */
		for (i = 0; i < readyList.length; i++) {
			readyList[i]();
		}
	}

	makeReady();
	return Yago;
});